using System;
using System.Configuration;
using System.Web;

using System.Xml;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington	
	Started:	2006-03-08	
	Status:		release	
	Version:	2.0.0
	Buidl:		20070828
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070524:	Modification for .Net 2.0
	20070703:	Removed net_2_0 directive (from compatibility build)
	20070828:	Minor mod.
	20070829:	Added ability to reference a link from another link
	20071119:	Added the Group property and mechanism
	-----------------------------------------------------------------------	*/

namespace umlungu.engineX.blogX {
	/// <summary>Enumeration to identify the source of the parameters</summary>
	public enum x_paramtype {
		/// <summary>Parameters are defined in the querystring</summary>
	    query = 1,
		/// <summary>Parameters are defined in form fields</summary>
	    form = 2,
		/// <summary>Parameters are defined in a link reference</summary>
	    link = 3
	}
	/// <summary>
	/// Description of the x_parameters class:
	/// <para>This class handles the parametisation of the values that are used in the displayX class</para>
	/// <para>In this class parameters can be defined in one of the following ways:</para>
	/// <para> - as query string parameters</para>
	/// <para> - as form fields</para>
	/// <para> - as attributes in a link reference</para>
	/// </summary>
	public class x_parameters {
		#region Invisible properties
		private static bool preload = false;	// nb if this is set true - need to make provision for sites that modify the links dynamically (eg after login)
		private HttpApplicationState application;
		private System.Web.UI.Page uipage;
		private char[] delimiter;
		#endregion

		#region Parameter String constants
		private const string delim 	= " ,.:";
		private const string NAME	= "name";
		private const string SPACES	= "spaces";
		private const string SPACE	= "space";
		private const string TOPICS	= "topics";
		private const string TOPIC	= "topic";
		private const string PAGES	= "pages";
		private const string BLOGS	= "blogs";
		private const string BLOG	= "blog";
		private const string BROKER	= "broker";
		private const string PROVIDER	= "provider";
		private const string PRINT	= "print";
		private const string SECURE	= "secure";
		private const string LOGIN = "login";
		private const string CHECK = "check";
		private const string TOKEN = "Token";
		private const string GROUP = "group";
		#endregion

		#region Error String constants
		private const string error_links_name = "Link has not been defined.";
		private const string error_links_list = "Links have not been loaded.";
		private const string error_link_elem = "Link has not been loaded.";
		private const string error_space_name = "Spaces are not defined ('spaces' parameter missing?)";
		private const string error_space_list = "Space list is empty ('spaces' parameter missing?)";
		private const string error_blogs_name = "Blogs are not defined ('blogs' parameter missing?)";
		private const string error_blogs_list = "Blog list is empty ('blogs' parameter missing?)";
		#endregion

		#region Visible properties
		private string name;
		/// <summary>Identifying name value</summary>
		/// <value>String value to identify the xsl, css and js to be loaded</value>
		public string Name {
			get { return name; }
			set { name = value; }
		}
		private bool secure;
		/// <summary>Secure (https) flag</summary>
		/// <value>Flag to indicate if the page is to be secured with https</value>
		public bool Secure {
			get { return secure; }
			set { secure = value; }
		}
		private string broker;
		/// <summary>Identifying broker value</summary>
		/// <value>String value to identify the broker. Used for special pages like sitemaps etc</value>
		public string Broker {
			get { return broker; }
			set { broker = value; }
		}
		private string provider;
		/// <summary>Identifying broker value</summary>
		/// <value>String value to identify the broker. Used for special pages like sitemaps etc</value>
		public string Provider {
			get { return provider; }
			set { provider = value; }
		}
		private string space;
		/// <summary>Spaces identifying value</summary>
		/// <value>String value to identify the (blog)spaces to be displayed</value>
		public string Space {
			get { return space; }
			set { space = value; }
		}
		private string spaces;
		/// <summary>Spaces identifying value</summary>
		/// <value>String value to identify the (blog)spaces to be displayed</value>
		public string Spaces {
			get { return spaces; }
			set { spaces = value; }
		}
		private string topic;
		/// <summary>Current topic</summary>
		/// <value>String value to identify the current (blog)topic</value>
		public string Topic {
			get { return topic; }
			set { topic = value; }
		}
		private string topics;
		/// <summary>Topics identifying value</summary>
		/// <value>String value to identify the (blog)topics to be displayed</value>
		public string Topics {
			get { return topics; }
			set { topics = value; }
		}
		private string blogs;
		/// <summary>Blogs identifying value</summary>
		/// <value>String value to identify the blogs to be displayed</value>
		public string Blogs {
			get { return blogs; }
			set { blogs = value; }
		}
		private string blog;
		/// <summary>Blog identifying value</summary>
		/// <value>String value to identify the blog to be displayed</value>
		public string Blog {
			get { return blog; }
			set { blog = value; }
		}
		private XmlElement link;
		/// <summary>The link reference element</summary>
		/// <value>XmlElement that contains the link reference</value>
		public XmlElement Link {
			get { return link; }
		}
		private XmlDocument links;
		/// <summary>The link reference document</summary>
		/// <value>XmlDocument that contains the link references</value>
		public XmlDocument Links {
			get { return links; }
		}
		private HttpRequest request;
		/// <summary>The http request object</summary>
		/// <value>HttpRequest of the instantiating page</value>
		public HttpRequest Request {
			get { return request; }
		}
		private x_paramtype type;
		/// <summary>The type of source used for the parameters</summary>
		/// <value>x_paramtype type enumeration</value>
		public x_paramtype Type {
			get { return type; }
		}
		private string linkName;
		/// <summary>Identifying link name value</summary>
		/// <value>String value to identify the link, empty if link not used</value>
		public string LinkName {
			get { return linkName; }
			set { linkName = value; loadLink(); }
		}
		private string device;
		/// <summary>Identifying link name value</summary>
		/// <value>String value to identify the link, empty if link not used</value>
		public string Device {
			get { return device; }
			set { device = value; }
		}
		/// <summary>The page element of the link</summary>
		/// <value>An XmlElement containg the page</value>
		public XmlElement LinkPage {
			get { return (XmlElement)link.SelectSingleNode("page"); }
		}
		/// <summary>The frame element of the link</summary>
		/// <value>An XmlElement containg the frame definition</value>
		public XmlElement LinkFrame {
			get { return (XmlElement)link.SelectSingleNode("frame"); }
		}
		private string section;
		/// <summary>The page section</summary>
		/// <value>A string containing the page section name</value>
		public string Section { 
			get { return section; }
			set { section = value; }
		}
		private string subsect;
		/// <summary>The page subsect</summary>
		/// <value>A string containing the page subsect name</value>
		public string Subsect { 
			get { return subsect; }
			set { subsect = value; }
		}
		private string process;
		/// <summary>The page process</summary>
		/// <value>A string containing the page process name</value>
		public string Process { 
			get { return process; }
			set { process = value; }
		}
		private string file;
		/// <summary>The page file</summary>
		/// <value>A string containing the page file name</value>
		public string File { 
			get { return file; }
			set { file = value; }
		}
		private bool login;
		/// <summary>Login flag</summary>
		/// <value>Flag to indicate if login required for the link (default is false)</value>
		public bool Login {
			get { return login; }
			set { login = value; }
		}
		private bool check;
		/// <summary>Check flag</summary>
		/// <value>Flag to indicate if patterns checks are to be skipped (default is true)</value>
		public bool Check {
			get { return check; }
			set { check = value; }
		}
		/// <value>A string containing the Token in the QueryString</value>
		public string Token {
			get { 
				var tok = getQuery(TOKEN, "");
				return (tok == "")? getForm(TOKEN, "") : tok;
			}
		}
		private string group;
		/// <summary>Identifying group value</summary>
		/// <value>String value to identify the group. Used for identifying a group that a user belongs to from login</value>
		public string Group {
			get { return group; }
			set { group = value; }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Description of the overloaded example method</overloads>
		/// <summary>Default constructor (older sites)</summary>
		/// <param name="app">The web application asp.net Application object</param>
		/// <param name="pge">The web application page object</param>
		public x_parameters(HttpApplicationState app, System.Web.UI.Page pge) {
			application = app;
			uipage = pge;
			request = uipage.Request;
			linkName = getQuery("link", "");
			initialise();
		}
		/// <summary>Default constructor</summary>
		/// <param name="def">The name of the default space</param>
		/// <param name="app">The web application asp.net Application object</param>
		/// <param name="pge">The web application page object</param>
		public x_parameters(string def, HttpApplicationState app, System.Web.UI.Page pge) {
			application = app;
			uipage = pge;
			request = uipage.Request;
			linkName = getQuery("link", "");
			initialise(def);
		}
		/// <summary>Default constructor</summary>
		/// <param name="app">The web application asp.net Application object</param>
		/// <param name="pge">The web application page object</param>
		/// <param name="lnk">Reference to a link definition</param>
		public x_parameters(HttpApplicationState app, System.Web.UI.Page pge, string lnk) {
			application = app;
			uipage = pge;
			request = uipage.Request;
			linkName = lnk;
			initialise();
		}
		#endregion

		#region Public methods
		/// <summary>Reload a set of parameters</summary>
		/// <param name="lnk">The link name</param>
		public void ReLoad(string lnk) {
			linkName = lnk;
			initialise(true);
		}
		/// <overloads>
		/// Indicates state of Yes/No QueryString parameter, 'yes'=true and 'no'=false
		/// </overloads>
		/// <summary>Only true when set to 'yes', all else is false</summary>
		/// <param name="param">The parameter name</param>
		public bool YesNo(string param) {
			return getParameterYesNo(param, false);	// default is 'no', ie ='yes' is true else false
		}
		/// <summary>Specify the default state (ie state when not yes or no)</summary>
		/// <param name="param">The parameter name</param>
		/// <param name="paramDefault">The default state (ie when parameter does not exist or if neither 'yes' or 'no'</param>
		public bool YesNo(string param, bool paramDefault) {
			return getParameterYesNo(param, paramDefault);
		}
		#endregion
		
		#region Protected methods
		/// <overloads>Loads a link reference from the links list document</overloads>
		/// <summary>Assumes the linkName is defined and throws exception if cannot load link</summary>
		private void loadLink() {
			if (links == null) {
				throw (new x_exception("error_links_list", error_links_list));
			}
			if (linkName == null | linkName == "") {
				throw (new x_exception("error_links_name", error_links_name));
			}
			// assign the link element
			link = (XmlElement)links.SelectSingleNode(String.Concat("/website/links/link[@name='", linkName, "']"));
			if (link != null) {	// check if there is a @link - then this is a reference to the actual link
				if (isThing(link.GetAttribute("link"))) {
					link = (XmlElement)links.SelectSingleNode(String.Concat("/website/links/link[@name='", link.GetAttribute("link"), "']"));
				}
			}
			if (link == null) {
				throw (new x_exception("error_link_elem", error_link_elem + ":" + linkName));
			}

			XmlElement linkPage = (XmlElement)link.SelectSingleNode("page");
			if (linkPage != null) {
				section = linkPage.GetAttribute("sect");
				subsect = linkPage.GetAttribute("sub");
				process = linkPage.GetAttribute("proc");
				file = linkPage.GetAttribute("file");
			}
		}
		/// <summary>Loads a specific link reference</summary>
		/// <param name="lnk">Reference to a link definition</param>
		private void loadLink(string lnk) {
			linkName = lnk;
			loadLink();
		}
		#endregion
				
		#region Private methods
		/// <summary>
		/// Loads the query string parameters. These are the following:
		///	- spaces:	the name(s) of the blogspace, or 'all'
		///	- topics:	the name(s) of the blogtopic, or 'all'
		///	- blogs:		the id(s) of the blogs, or 'all'. The 'page' parameter can also be used, but 'blogs' takes precedence
		/// Note that multiple values are comma delimited.
		/// </summary>
		private void initialise() {
			initialise(false);
		}
		private void initialise(string defaultSpace) {
			initialise(false, defaultSpace);
		}
		/// <summary>Primarily for older sites</summary>
		private void initialise(bool reload) {
			x_config config = new x_config();
			initialise(reload, config.Space);
		}
		private void initialise(bool reload, string defaultSpace) {
			// initialise some basic properties
			device = getQuery("device", "");
			delimiter = delim.ToCharArray();
			if (!reload)
				loadLinks();
			setType();
			// now get/initialise the parameter properties
			string pages;
			spaces = getParameter(SPACES, defaultSpace);
			if (spaces == null)
				throw(new x_exception("error_space_name", error_space_name));
			space = getParameter(SPACE, getSpaceName());
			name = getParameter(NAME, defaultSpace);
			if (name == null)
				name = space;	// defaults to the name of the space (first in the list)
			secure = getParameterYesNo(SECURE, false);
			login = getParameterYesNo(LOGIN, false);
			check = getParameterYesNo(CHECK, true);
			broker = getParameter(BROKER, "");
			provider = getParameter(PROVIDER, "");
			topic = getParameter(TOPIC, "");
			topics = getParameter(TOPICS, "");
			pages = getParameter(PAGES, "");
			blogs = getParameter(BLOGS, "");
			if (blogs == "" && pages != "")
				blogs = pages;
			blog = getParameter(BLOG, getBlogID());
			group = getParameter(GROUP, "");
		}
	/* not sure if private or protected required - comment out private for now
		/// <overloads>Loads a link reference from the links list document</overloads>
		/// <summary>Assumes the linkName is defined and throws exception if cannot load link</summary>
		private void loadLink() {
			if (links == null)
				throw (new x_exception("error_links_list", error_links_list));
			if (linkName == null | linkName == "")
				throw (new x_exception("error_links_name", error_links_name));
			link = (XmlElement)links.SelectSingleNode(String.Concat("/website/links/link[@name='", linkName, "']"));
			if (link == null)
				throw (new x_exception("error_link_elem", error_link_elem + ":" + linkName));
			XmlElement linkPage = (XmlElement)link.SelectSingleNode("page");
			if (linkPage != null) {
				section = linkPage.GetAttribute("sect");
				subsect = linkPage.GetAttribute("sub");
				process = linkPage.GetAttribute("proc");
				file = linkPage.GetAttribute("file");
			}
		}
		/// <summary>Loads a specific link reference</summary>
		/// <param name="lnk">Reference to a link definition</param>
		private void loadLink(string lnk) {
			linkName = lnk;
			loadLink();
		}
	*/
		/// <summary>
		/// Checks and return parameter (or default value)
		/// </summary>
		private string getParameter(string param, string def) {
			string thisparam = "";
			switch (type) {
				case x_paramtype.link:	thisparam = getLink(param, def);	break;
				case x_paramtype.form:	thisparam = getForm(param, def);	break;
				case x_paramtype.query:	thisparam = getQuery(param, def);	break;
				default:				thisparam = getQuery(param, def);	break;
			}
			return thisparam;
		}
		/// <summary>Check and return query parameter (or default value)</summary>
		private string getQuery(string param, string def) {
			return (Request.QueryString[param] == null)? def: Request.QueryString[param];
		}
		/// <summary>Check and return form parameter (or default value)</summary>
		private string getForm(string param, string def) {
			return (Request.Form[param] == null)? def: Request.Form[param];
		}
		/// <summary>Check and return link parameter (or default value)</summary>
		private string getLink(string param, string def) {
			string thisparam = "";
			if (link == null)
				throw(new x_exception("error_link_elem", error_link_elem));
			// first check in the attributes of the link node itself (eg feed, poll, tip etc)
			if (param != "name")	// dont check parent if 'name'
				thisparam = link.GetAttribute(param);
			if (isNothing(thisparam)) {
				XmlElement paramnode = (XmlElement)link.SelectSingleNode("blog|file|service");
				if (paramnode != null) {
					thisparam = paramnode.GetAttribute(param);
					if (isNothing(thisparam)) {	// look in parent if null
						paramnode = (XmlElement)link.ParentNode.SelectSingleNode("blog|file|service");
						if (paramnode != null)
							thisparam = paramnode.GetAttribute(param);
					}
				}
			}
			if (String.Compare(thisparam, "querystring") == 0) {
				return getQuery(param, def);
			} else if (String.Compare(thisparam, "query") == 0) {
				thisparam =	getParameter(String.Concat(param, "query"), "query");
				return getQuery(thisparam, def);
			} else {
				return (isNothing(thisparam)) ? def : thisparam;
			}
		}
		/// <summary>
		/// Checks and returns the first spaces name in the delimited list.
		/// </summary>
		private bool getParameterYesNo(string param, bool paramDefault) {
			string yn = "";
			switch (type) {
				case x_paramtype.link:	yn = getLink(param, "");	break;
				case x_paramtype.form:	yn = getForm(param, "");	break;
				case x_paramtype.query:	yn = getQuery(param, "");	break;
				default:				yn = getQuery(param, "");	break;
			}
			if (yn == "yes")
				return true;
			else if  (yn == "no")
				return false;
			else
				return paramDefault;
		}
		/// <summary>
		/// Sets the type of source for the parameters
		/// </summary>
		private void setType() {
			if (isThing(linkName)) {
				type = x_paramtype.link;
				loadLink();
			}
			//else if (linkName != null && linkName != "")	// how to define form parameters?
				//type = x_paramtype.form;
			else
				type = x_paramtype.query;
		}
		
		/// <summary>
		/// Checks and returns the first spaces name in the delimited list.
		/// </summary>
		private string getBlogID() {
			if (blogs == null)
				throw(new x_exception("error_blogs_name", error_blogs_name));
			string[] blogList = blogs.Split(delimiter);
			if (blogList.Length <= 0)
				throw(new x_exception("error_blogs_list", error_blogs_list));
			// return first name on the list (unless "all" - then returns '0')
			return (blogList[0] == "all")? "0" : blogList[0];
		}
		/// <summary>
		/// Checks and returns the first spaces name in the delimited list.
		/// </summary>
		private string getSpaceName() {
			if (spaces == null)
				throw(new x_exception("error_space_name", error_space_name));
			string[] spaceList = spaces.Split(delimiter);
			if (spaceList.Length <= 0)
				throw(new x_exception("error_space_list", error_space_list));
			// return first name on the list
			return spaceList[0];
		}
		/// <summary>Checks if a string is empty or null.</summary>
		private bool isNothing(string thisstring) {
			return (thisstring == null || String.Compare(thisstring, "") == 0)? true : false;
		}
		private bool isThing(string thisstring) {
			return (thisstring != null && String.Compare(thisstring, "") != 0)? true : false;
		}
		/// <summary>Gets the links xml document.</summary>
		private void loadLinks() {
			if (preload)
				links = (XmlDocument) application["DisplayX"];
			else {
				links = new XmlDocument();
				x_config config = new x_config();
				links.Load(uipage.Server.MapPath(String.Concat(config.Path, "templates/displayX.xml")));
			}
		}
		#endregion
	}
}
